package com.cygsunri.wisdompark.callback.service;

import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.cygsunri.common.EnumConstant;
import com.cygsunri.history.service.GeneralDataService;
import com.cygsunri.measurement.entity.MeasurementValue;
import com.cygsunri.scada.base.BasePSR;
import com.cygsunri.template.entity.psr.DeviceTemplate;
import com.cygsunri.template.entity.psr.PSRTemplateMapping;
import com.cygsunri.template.service.DeviceTemplateDataService;
import com.cygsunri.template.service.PSRTemplateMappingService;
import com.cygsunri.wisdompark.callback.entity.DeviceInfo;
import com.cygsunri.wisdompark.callback.entity.DeviceTemplateData;
import com.cygsunri.wisdompark.util.EnumUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.math.NumberUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.ListOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.stereotype.Service;

import java.lang.reflect.Method;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

@Slf4j
@Service
public class ProcessingDataService implements InitializingBean {

    @Autowired
    private RedisTemplate<String, String> redisTemplate;
    @Autowired
    private InfluxDBService influxDBService;
    @Autowired
    private PSRTemplateMappingService psrTemplateMappingService;
    @Autowired
    private DeviceTemplateDatasService deviceTemplateDatasService;
    @Autowired
    private GeneralDataService generalDataService;


    private HashOperations<String, String, String> hashOp;
    private ListOperations<String, String> listOp;


    @Override
    public void afterPropertiesSet() throws Exception {
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(new StringRedisSerializer());
        hashOp = redisTemplate.opsForHash();
        listOp = redisTemplate.opsForList();
    }

    /**
     * 根据设备类型id查找实时数据
     *
     * @param deviceTemplateId 设备类型ID
     * @param name 点位
     * @return DeviceTemplateData
     */
    public Map<String,Double> getDataByDeviceTemplateID(String deviceTemplateId,String name){
        Map<String,Double> result = new HashMap<String,Double>();
        List<PSRTemplateMapping> psrTemplateMappings = psrTemplateMappingService.getPSRTemplateMappingListByDeviceTemplateId(deviceTemplateId);
        List<String> psrIds = psrTemplateMappings.stream().map(PSRTemplateMapping::getPsrID).collect(Collectors.toList());
        for(String psrId:psrIds) {
            MeasurementValue measurementValue = new MeasurementValue().setO(new BasePSR().setId(psrId)).setName(name).setRdb(true);
            measurementValue = generalDataService.getValue(measurementValue);
            result.put(psrId, measurementValue == null ? NumberUtils.DOUBLE_ZERO:measurementValue.getData());
        }
        return result;
    }



    /**
     * 通用保存实时数据和历史数据
     *
     * @param devCode 设备ID
     * @param
     * @return DeviceTemplateData
     */
    public void saveData(String devCode, Map<String, Object> map, String source) throws Exception {
        //1 获取到设备deviceId
        DeviceInfo deviceInfo = deviceTemplateDatasService.findDeviceByDevCode(devCode);
        if (deviceInfo == null) {
            throw new Exception("找不到devCode:" + devCode + "的scd设备参数,map:"+map.toString()+",source:"+source);
        }
        String psrID = deviceInfo.getPsrId();
        DeviceTemplate deviceTemplate = psrTemplateMappingService.getDeviceTemplateByMapping(psrID);
        if (deviceTemplate == null) {
            throw new Exception("找不到devCode:" + devCode +deviceInfo.getComments()+ "的DeviceTemplate,map:"+map.toString()+",source:"+source);
        }
        List<DeviceTemplateData> ycList = deviceTemplateDatasService.findDataByPsrID(psrID);
        if (CollectionUtils.isEmpty(ycList)) {
            throw new Exception("找不到devCode:" + devCode +deviceInfo.getComments() + ",DeviceTemplateId:" + deviceTemplate.getId() + "的deviceTemplateDatas,map:"+map.toString()+",source:"+source);
        }
        Map<String, Object> saveMap = new HashMap();
        Map<String, MeasurementValue> measurementValueMap = new HashMap<>();
        Map<String, Object> notFouldMap = new HashMap<>();
        Map<String, DeviceTemplateData> ycMap = ycList.stream().collect(Collectors.toMap(DeviceTemplateData::getName, Function.identity()));
        for (DeviceTemplateData data : ycList) {
            MeasurementValue measurementValue = new MeasurementValue().setO(new BasePSR().setId(psrID)).setName(data.getName()).setRdb(true);
            measurementValue = generalDataService.getValue(measurementValue);
            measurementValueMap.put(measurementValue.getName(), measurementValue);
        }
        map.forEach((k, v) -> {
            if (measurementValueMap.containsKey(k)) {
                if (EnumConstant.saveSource.tuya.getValue().equals(source)) {
                    getyadaData(deviceTemplate, saveMap, measurementValueMap, ycMap, k, v,devCode);
                } else {
                    saveMap.put(measurementValueMap.get(k).getKey(), v);
                }
            } else {
                if (!"time".equals(k)) {
                    notFouldMap.put(k, v);
                }
            }
        });
        if (!saveMap.isEmpty()) {
            log.info("saveData {}:{}", deviceInfo.getComments(), saveMap.toString());
            long time = (long) map.get("time");
            saveMap.forEach((k, v) -> {
                saveMean(k, Double.parseDouble(v.toString()), time);
                influxDBService.saveAnalogs(k, Double.parseDouble(v.toString()), time);
            });
        }
        if (!notFouldMap.isEmpty()) {
            log.error("notFouldMap {} {}:{}", devCode,deviceInfo.getComments(), notFouldMap.toString());
        }
    }

    /**
     * 处理雅达数据
     */
    private void getyadaData(DeviceTemplate deviceTemplate, Map<String, Object> saveMap, Map<String, MeasurementValue> measurementValueMap, Map<String, DeviceTemplateData> ycMap, String k, Object v,String devCode) {
        DeviceTemplateData temp = ycMap.get(k);
        String dataType = temp.getDataType();
        try {
            if (EnumConstant.tuyaDataType.valueOf(dataType).getValue()) {
                Object value;
                if (dataType.equals(EnumConstant.tuyaDataType.Boolean.getName())) {
                    boolean b = Boolean.parseBoolean(v.toString());
                    value = b ? 1 : 0;
                    saveMap.put(measurementValueMap.get(k).getKey(), value);
                } else if (dataType.equals(EnumConstant.tuyaDataType.Enum.getName())) {

                    value = EnumUtil.getValueByClassName("com.cygsunri.common.EnumConstant", deviceTemplate.getName() + "_" + k, v.toString());
                    if (value == null) {
                        throw new Exception("找不到涂鸦设备devCode:" + devCode + "的枚举类" + k + "的" + v.toString() + "对应的值");
                    }
                    saveMap.put(measurementValueMap.get(k).getKey(), value);
                } else if (dataType.equals(EnumConstant.tuyaDataType.Raw.getName())) {
                    //todo
                    throw new Exception("涂鸦设备devCode:" + devCode + "的类型为Raw");
                } else if (dataType.equals(EnumConstant.tuyaDataType.String.getName())) {
                    //todo
                    throw new Exception("涂鸦设备devCode:" + devCode + "的类型为String");
                }
            } else {
                saveMap.put(measurementValueMap.get(k).getKey(), v);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 存数据至redis
     */
    public void saveMean(String code, Double data, long time) {
        String key = String.format("mean/%1$s", code);
        Map<String, String> field_value = new HashMap<>();
        field_value.put("0", String.valueOf(data));
        field_value.put("0/t", String.valueOf(time));
        field_value.put("0/q", "0");
        hashOp.putAll(key, field_value);
    }

}
